package state

import (
	"fmt"
	"math/big"
	"sort"

	"hundsun.com/hsl/hschain/common/log"
	"hundsun.com/hsl/hschain/contract/evm/ext"
	"hundsun.com/hsl/hschain/contract/evm/ext/common"
	ecom "hundsun.com/hsl/hschain/executor/common"
	pbcom "hundsun.com/hsl/hschain/protos/common"
	"hundsun.com/hsl/hschain/protos/execute"
)

// EVMStateDB evm 数据库操作后端
type EVMStateDB struct {
	// 执行器上下文
	execCtx *ecom.ExecContent

	// 合约执行过程中退回的资金(目前设计中暂时不考虑退回资金)
	refund uint64

	// 缓存状态对象
	stateObjects map[common.Address]*EVMStateObject

	// 版本号，用于标识数据变更版本
	snapshots []*Snapshot
	versionID int

	// 存储makeLogN指令对应的日志数据
	logs   []*ext.EvmLog
	txHash common.Hash

	// 存储sha3指令对应的数据，仅用于debug日志
	preimages map[common.Hash][]byte

	// 是否只读(用于变量读写标记)
	readOnly bool

	// 当前区块高度
	blockHeight uint64
}

// NewEVMStateDB new state db
func NewEVMStateDB(ctx *ecom.ExecContent, readOnly bool) *EVMStateDB {
	s := &EVMStateDB{
		execCtx:      ctx,
		refund:       0,
		stateObjects: make(map[common.Address]*EVMStateObject),
		preimages:    make(map[common.Hash][]byte),
		readOnly:     readOnly,
		blockHeight:  ctx.GetHeight(),
	}
	return s
}

// SetTxHash set tx hash to EVMStateDB
func (s *EVMStateDB) SetTxHash(hash common.Hash) {
	s.txHash = hash
}

// CreateAccount 创建一个新的合约账户对象
func (s *EVMStateDB) CreateAccount(addr common.Address) {
	so := s.getStateObject(addr)
	if so == nil {
		// 这种情况下为新增合约账户, TODO还未进行code的设置
		so = NewEVMStateObject(addr, s, s.readOnly)
		s.stateObjects[addr] = so
	}
}

// getStateObject 从缓存中获取或加载合约账户
func (s *EVMStateDB) getStateObject(addr common.Address) *EVMStateObject {
	if so, ok := s.stateObjects[addr]; ok {
		return so
	}
	so := LoadEVMStateObject(addr, s, s.readOnly)
	if so == nil || so.Empty() {
		return nil
	}
	s.stateObjects[addr] = so
	return so
}

// SubBalance 从账户中减去一笔金额
func (s *EVMStateDB) SubBalance(addr common.Address, amount *big.Int) {
	so := s.getStateObject(addr)
	if so == nil {
		log.Debugf("EVMStateDB subBalance not find account addr %x", addr)
		return
	}
	so.SubBalance(amount)
	log.Tracef("EVMStateDB subBalance addr %x, amount %v", addr, amount.Uint64())
}

// AddBalance 从账户中新增一笔金额
func (s *EVMStateDB) AddBalance(addr common.Address, amount *big.Int) {
	so := s.getStateObject(addr)
	if so == nil {
		// 这种情况下为新增账户
		so = NewEVMStateObject(addr, s, s.readOnly)
		s.stateObjects[addr] = so
	}
	so.AddBalance(amount)
	log.Tracef("EVMStateDB addBalance addr %x,  amount %v", addr, amount.Uint64())
}

// GetBalance ...
func (s *EVMStateDB) GetBalance(addr common.Address) *big.Int {
	so := s.getStateObject(addr)
	if so == nil {
		return common.Big0
	}
	return so.GetBalance()
}

// GetNonce 目前中没有保留账户的nonce信息，这里临时添加到合约账户中；
// 所以，目前只有合约对象有nonce值
func (s *EVMStateDB) GetNonce(addr common.Address) uint64 {
	so := s.getStateObject(addr)
	if so != nil {
		return so.GetNonce()
	}
	return 0
}

// SetNonce 设置nonce值
func (s *EVMStateDB) SetNonce(addr common.Address, nonce uint64) {
	so := s.getStateObject(addr)
	if so != nil {
		so.SetNonce(nonce)
	}
}

// GetCodeHash 获取代码哈希
func (s *EVMStateDB) GetCodeHash(addr common.Address) common.Hash {
	so := s.getStateObject(addr)
	if so != nil && len(so.codeHash.Bytes()) != 0 {
		return so.codeHash
	}
	return common.Hash{}
}

// GetCode 获取代码内容
func (s *EVMStateDB) GetCode(addr common.Address) []byte {
	log.Tracef("EVMStateDB GetCode addr %x", addr)
	so := s.getStateObject(addr)
	if so != nil && so.info != nil {
		return so.info.GetCode()
	}
	return nil
}

// SetCode 设置代码内容
func (s *EVMStateDB) SetCode(addr common.Address, code []byte) {
	log.Tracef("EVMStateDB SetCode addr %x", addr)
	so := s.getStateObject(addr)
	if so != nil {
		so.SetCode(code)
	}
}

// GetCodeSize 获取合约代码自身的大小
func (s *EVMStateDB) GetCodeSize(addr common.Address) int {
	code := s.GetCode(addr)
	if code != nil {
		return len(code)
	}
	return 0
}

// SetAbi 设置ABI内容
func (s *EVMStateDB) SetAbi(addr common.Address, abi string) {
	so := s.getStateObject(addr)
	if so != nil {
		so.SetAbi(abi)
	}
}

// GetAbi 获取ABI
func (s *EVMStateDB) GetAbi(addr common.Address) string {
	so := s.getStateObject(addr)
	if so != nil {
		return so.GetAbi()
	}
	return ""
}

// AddRefund  合约自杀或SSTORE指令时，返还Gas
func (s *EVMStateDB) AddRefund(gas uint64) {
	s.refund += gas
}

// SubRefund removes gas from the refund counter.
func (s *EVMStateDB) SubRefund(gas uint64) {
	if gas > s.refund {
		log.Warnf("Refund counter below zero (gas: %d > refund: %d)", gas, s.refund)
		return
	}
	s.refund -= gas
}

// GetRefund 获取奖励
func (s *EVMStateDB) GetRefund() uint64 {
	return s.refund
}

// GetCommittedState retrieves a value from the committed account storage trie.
// 这里GetCommittedState与GetState取出来的应该是一样的(不同于eth中的将gas给到矿工的模式)
func (s *EVMStateDB) GetCommittedState(addr common.Address, key common.Hash) common.Hash {
	return s.GetState(addr, key)
}

// GetState 加载合约状态数据
func (s *EVMStateDB) GetState(addr common.Address, key common.Hash) common.Hash {
	so := s.getStateObject(addr)
	if so != nil {
		return so.GetState(key)
	}
	return common.Hash{}
}

// SetState 修改合约状态数据
func (s *EVMStateDB) SetState(addr common.Address, key common.Hash, value common.Hash) {
	so := s.getStateObject(addr)
	if so != nil {
		so.SetState(key, value)
	}
}

// Suicide SELFDESTRUCT 合约对象自杀
// 合约自杀后，合约对象依然存在，只是无法被调用，也无法恢复
func (s *EVMStateDB) Suicide(addr common.Address) bool {
	so := s.getStateObject(addr)
	if so != nil {
		return so.Suicide()
	}
	return false
}

// HasSuicided 判断此合约对象是否已经自杀,自杀的合约对象是不允许调用的
func (s *EVMStateDB) HasSuicided(addr common.Address) bool {
	so := s.getStateObject(addr)
	if so != nil {
		return so.HasSuicided()
	}
	return false
}

// Exist 判断合约对象是否存在
func (s *EVMStateDB) Exist(addr common.Address) bool {
	return s.getStateObject(addr) != nil
}

// Empty 判断合约对象是否为空
func (s *EVMStateDB) Empty(addr common.Address) bool {
	so := s.getStateObject(addr)
	return so == nil || so.Empty()
}

// AddressInAccessList TODO 后面考虑实现
func (s *EVMStateDB) AddressInAccessList(addr common.Address) bool {
	return true
}

// SlotInAccessList TODO 后面考虑实现
func (s *EVMStateDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) {
	return true, true
}

// AddAddressToAccessList TODO 后面考虑实现
func (s *EVMStateDB) AddAddressToAccessList(addr common.Address) {}

// AddSlotToAccessList TODO 后面考虑实现
func (s *EVMStateDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {}

// RevertToSnapshot 将数据状态回滚到指定快照版本(目前执行某个交易失败直接丢弃该条交易的修改内容)
func (s *EVMStateDB) RevertToSnapshot(version int) {
	if version >= len(s.snapshots) {
		return
	}
	ver := s.snapshots[version]
	// 如果版本号不对，回滚失败
	if ver == nil || ver.id != version {
		log.Fatal(fmt.Errorf("Snapshot id %v cannot be reverted", version).Error())
		return
	}
	// 从最近版本开始回滚,目前机制下只回滚一个版本
	for index := len(s.snapshots) - 1; index >= version; index-- {
		s.snapshots[index].revert()
	}
	// 只保留回滚版本之前的版本数据
	s.snapshots = s.snapshots[:version]
	s.versionID = version
}

// Snapshot 对当前的数据状态打快照，并生成快照版本号，方便后面回滚数据
func (s *EVMStateDB) Snapshot() int {
	id := s.versionID
	s.versionID++
	s.snapshots = append(s.snapshots, &Snapshot{id: id, sdb: s})
	return id
}

func (s *EVMStateDB) addChange(entry DataChange) {
	if len(s.snapshots) > 0 {
		s.snapshots[len(s.snapshots)-1].append(entry)
	}
}

// GetContractEvents 获取合约产生的事件
func (s *EVMStateDB) GetContractEvents() (events []*pbcom.ContractEvent) {
	for _, clog := range s.logs {
		event := &pbcom.ContractEvent{}
		event.ContractAddress = clog.Address.ToNoPrefixHex()
		event.TransactionHash = s.txHash.Bytes()
		event.BlockNumber = s.blockHeight
		event.Data = clog.Data
		for i, topic := range clog.Topics {
			switch i {
			case 0:
				event.IndexedParamFirst = topic.String()
			case 1:
				event.IndexedParamSecond = topic.String()
			case 2:
				event.IndexedParamThird = topic.String()
			}
			if i >= 2 {
				break
			}
		}
		events = append(events, event)
	}
	return events
}

// GetContractKV 获取本次操作所引起的状态数据变更
// 合约内部会调用其它合约，也会产生数据变更，所以这里返回的数据，不止是一个合约的数据。
func (s *EVMStateDB) GetContractKV() (kvSet []*pbcom.KeyValue) {
	for _, acc := range s.stateObjects {
		kvSet = append(kvSet, acc.GetContractKV()...)
	}
	if len(kvSet) > 0 {
		sort.Slice(kvSet, func(i, j int) bool {
			return string(kvSet[i].Key) < string(kvSet[j].Key) // 进行排序
		})
	}
	return
}

// GetReadWriteSign 获取合约状态读写状态
func (s *EVMStateDB) GetReadWriteSign() []*execute.RWStatus {
	var res []*execute.RWStatus
	for _, so := range s.stateObjects { // 不同账户对象的变量操作不需要去重
		res = append(res, convert2RWStatus(so.GetReadWriteSign())...)
	}
	return res
	//mp := make(map[string]bool)
	//for _, so := range s.stateObjects {
	//	for k, v := range so.GetReadWriteSign() {
	//		if sign, ok := mp[k]; ok && sign {
	//			continue
	//		}
	//		mp[k] = v
	//	}
	//}
	//return convert2RWStatus(mp)
}

func convert2RWStatus(mp map[string]bool) []*execute.RWStatus {
	var rwStatus []*execute.RWStatus
	for k, v := range mp {
		status := execute.RwStatusType_ReadStatus
		if v {
			status = execute.RwStatusType_WriteStatus
		}
		rwStatus = append(rwStatus, &execute.RWStatus{Key: []byte(k), RwStatus: status})
	}
	return rwStatus
}

// AddLog 指令对应的具体操作用于生成receipt
func (s *EVMStateDB) AddLog(clog *ext.EvmLog) {
	log.Tracef("EVMStateDB::AddLog txhash %s, blockHeight %d", s.txHash.Hex(), s.blockHeight)
	s.addChange(addLogChange{
		txhash: s.txHash,
	})
	clog.TxHash = s.txHash
	s.logs = append(s.logs, clog)
}

// AddPreimage 存储sha3指令对应的数据(目前暂未使用)
func (s *EVMStateDB) AddPreimage(hash common.Hash, data []byte) {
	if _, ok := s.preimages[hash]; !ok {
		s.addChange(addPreimageChange{
			hash: hash,
		})
		ndata := make([]byte, len(data))
		copy(ndata, data)
		s.preimages[hash] = ndata
	}
}

// ForEachStorage 遍历存储 测试用
func (s *EVMStateDB) ForEachStorage(common.Address, func(common.Hash, common.Hash) bool) error {
	return nil
}

// PrintLogs 合约执行完毕之后打印合约生成的日志
func (s *EVMStateDB) PrintLogs() {
	log.Debugf("PrintLogs", "item number %d, txhash %s", len(s.logs), s.txHash.Hex())
	for _, clog := range s.logs {
		clog.PrintLog()
	}
}
